﻿namespace Jsl.StyleCop.CSharp.RefactoringRules
{
    using Microsoft.StyleCop;
    using Microsoft.StyleCop.CSharp;

    /// <summary>
    /// A collect of rules for catching smelly code.
    /// </summary>
    [SourceAnalyzer(typeof(CsParser))]
    public class RuleAnalyzer : SourceAnalyzer
    {
        /// <summary>
        /// You shouldn't compare a boolean value to a boolean constant.
        /// </summary>
        public static readonly string DontCompareToBooleanConstant = "DontCompareToBooleanConstant";

        /// <summary>
        /// If you set a variable, you should use it before it is overwritten.
        /// </summary>
        public static readonly string LocalVariableAssignmentUnused = "LocalVariableAssignmentUnused";

        /// <summary>
        /// Local variables should be set where they are declared.
        /// </summary>
        public static readonly string LocalVariablesShouldBeSetWhereTheyAreDeclared = "LocalVariablesShouldBeSetWhereTheyAreDeclared";

        /// <summary>
        /// Move variable declarations to the level of their usage.
        /// </summary>
        public static readonly string MoveVariableDeclarationToUsageLevel = "MoveVariableDeclarationToUsageLevel";

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="RuleAnalyzer"/> class.
        /// </summary>
        public RuleAnalyzer()
        {
        }
        #endregion Constructors

        #region Public Methods
        /// <summary>
        /// Analyzes the document.
        /// </summary>
        /// <param name="document">The document.</param>
        public override void AnalyzeDocument(CodeDocument document)
        {
            CsDocument csharpDocument = (CsDocument)document;

            // Run the non graph rules.
            DontCompareToBooleanConstant nonGraphRule = new DontCompareToBooleanConstant(this);
            nonGraphRule.Document = csharpDocument;
            nonGraphRule.AnalyzeDocument();

            CsRuleBase<RuleAnalyzer>[] rules = new CsRuleBase<RuleAnalyzer>[]
            {
                new LocalVariableAssignmentUnused(this, csharpDocument),
                new LocalVariablesShouldBeSetWhereTheyAreDeclared(this, csharpDocument),
                new MoveVariableDeclarationToUsageLevel(this, csharpDocument)
            };

            csharpDocument.WalkDocument(this.AnalyzeElement, rules);
        }
        #endregion Public Methods

        #region Private Methods
        private static void RunRules(CodeUnitGraph graph, CsElement element, CsRuleBase<RuleAnalyzer>[] rules)
        {
            foreach (var rule in rules)
            {
                rule.AnalyzeGraph(element, graph);
            }
        }

        private bool AnalyzeElement(CsElement element, CsElement parentElement, CsRuleBase<RuleAnalyzer>[] rules)
        {
            switch (element.ElementType)
            {
                case ElementType.Accessor:                  // Property get/set, event add/remove and indexers.
                case ElementType.Constructor:
                case ElementType.ConstructorInitializer:    // Static constructor?
                case ElementType.Destructor:
                case ElementType.Method:
                    CodeUnitGraph graph = FlowGraph.BuildFlowGraph(element);
                    RuleAnalyzer.RunRules(graph, element, rules);
                    break;
            }

            return true;
        }
        #endregion Private Methods
    }
}